
// DRL / Park / Turn driver for Learning Vehicle
//
// Current is set by hardware to nominally 400mA
// Open circuit on PWM pin will give nominal current
// 50% duty cycle PWM will give nominal current
// 100% duty cycle PWM will give double current
// 0% duty cycle PWM will give zero current
//
// Maximum PWM frequency is 300 Hz
//
// Park current is 40mA
// DRL current is 200mA
// Turn current is 400mA
// Park overrides DRL
// Turn overrides Park and DRL

// Cal values
// 01    0x20  (0xC20) 
// 02
// 03    0x2e  (0xC2e)
// 04    0x2e  (0xC2e)

#pragma chip = PIC10F222

#include "hexcodes.h"
#include "debounce.h"

// History
// 1.0   First version
// 2.0   Changed to open Source drive
// 2.1   Doubled loop frequency
//       Enabled WDT
//       Retimed turn holdoff to be 1s with new loop time


#define  Revision       0x21  // Revision Code
#define  RevAddress     0x1FE // Location for revision code

#define  ResetAddress   0x1FF // 10F222 location for oscillator calibration
#define  CalValue       0x00  // oscilator calibration value

#pragma  cdata[RevAddress]  = __MOVLW(Revision)
#pragma  cdata[ResetAddress]= __MOVLW(CalValue)



// CONFIG
// 0b 0000 000x xxxx
//            | |||+-- 1=8MHz, 0=4MHz
//            | ||+--- 1=MClr Pullup disabled, 0=Enabled
//            | |+---- 1=WDT enabled, 0=Disabled
//            | +----- 1=CodeProt Off, 0=On
//            +------- 1=GP3 is MClr, 0=GP3 is I/O

// WDT has a period of 18mS - prescaler assigned to TMR0
// Main loop runs at 244Hz = 4mS.
// Therefore we can use the WDT, clear it every loop


#pragma config = 0b000000001110

/* Pin   Pin   Pragma
   SOT   DIP   Port  Dir   Name
   N/A   1     N/C         Not connected      
   N/A   6     N/C         Not connected
   5     2     Vdd         Power
   2     7     Vss         Ground
   1     5     GP0   Out   PWM
   3     4     GP1   In    Park
   4     3     GP2   In    DRL
   6     8     GP3   In    Turn
*/

#define  PWM_pin  GP0
#define  PWM_tris TRISGPIO.0
#define  Park_pin GP1
#define  DRL_pin  GP2
#define  Turn_pin GP3

#define  PWM_HIZ     0b00001111
#define  PWM_LOZ     0b00001110

char pwmSetting;

// Current definitions
// value = (current_required/182) * 128

#define OFF_CURRENT  255
#define PARK_CURRENT 230
#define DRL_CURRENT  0
#define TURN_CURRENT 0


#include "debounce.c"

// Park overrides DRL
// Turn overrides Park
// If Turn is active, DRL is disabled for a time period after Turn is deactivated


#define FILTERED_PARK   filteredData.1
#define FILTERED_DRL    filteredData.2
#define FILTERED_TURN   filteredData.3

#define STATE_NORMAL    0
#define STATE_TURN      1
#define STATE_CANCEL    2
#define STATE_HOLDOFF   3

#define MODE_OFF       0
#define MODE_DRL       1
#define MODE_PARK      2
#define MODE_TURN      3

#define TURN_COUNT   244
char state, mode, turnCount;

void updateSetting( void )
{
   char filteredData;
   // scan inputs, choose pwmSetting as appropriate

   filteredData = debounce();  

   // DRL input not active

   switch( state )
   {
      case( STATE_NORMAL ):
         mode = MODE_OFF;
         if( FILTERED_DRL )
            mode = MODE_DRL;
         if( FILTERED_PARK )
            mode = MODE_PARK;
         if( FILTERED_TURN )
         {
            mode = MODE_TURN;
            state = STATE_TURN;
         }
         break;

      case( STATE_TURN ):
         mode = MODE_TURN;
         if( !FILTERED_TURN )
            state = STATE_CANCEL;
         break;

      case( STATE_CANCEL ):
         turnCount = 0;         
         state = STATE_HOLDOFF;
         break;

      case( STATE_HOLDOFF ):
         mode = MODE_OFF;

         if( FILTERED_TURN )
         {
            state = STATE_TURN;
            mode = MODE_TURN;
         }

         if( FILTERED_PARK )
            mode = MODE_PARK;

         turnCount++;
         if(turnCount >= TURN_COUNT )
            state = STATE_NORMAL;

         break;

      default:
         state = STATE_NORMAL;
         mode = MODE_OFF;
         break;
   }   

   switch( mode )
   {
      case( MODE_DRL ):
         pwmSetting = DRL_CURRENT;
         break;

      case( MODE_PARK ):
         pwmSetting = PARK_CURRENT;
         break;

      case( MODE_TURN ):
         pwmSetting = TURN_CURRENT;
         break;

      case( MODE_OFF ):
      default:
         pwmSetting = OFF_CURRENT;
         break;
   }
}

#if 0
void delay( void )
{
   char loop;
   for( loop=255; loop>0; loop-- );
}
#endif

#if 0
void debug( void )
{
   while( 1 )
   {
      char filteredData;
      filteredData = debounce();

      PWM_pin = 0;
      delay();
      delay();
      delay();
      delay();

      PWM_pin = filteredData.1;
      delay();

      PWM_pin = filteredData.2;
      delay();

      PWM_pin = filteredData.3;
      delay();
   }
}
#endif
  

void main( void )
{
   
// OPTION
// 0b WWWW WWWW
//    |||| |+++-- PS2-0 Prescaler value
//    |||| +----- Prescaler 1=WDT, 0=TMR0
//    |||+------- Timer increment 1=Hi to Lo transition, 0=Lo to Hi
//    ||+-------- Timer source 1=Pin, 0=Fosc
//    |+--------- Weak pullups 1=Disabled, 0=Enabled (GP0,1,3)
//    +---------- Wake up on change 1=Disabled, 0=Enabled (GP0,1,3)

//   OPTION = 0b01000100;
   OPTION = 0b01000011;


// Fosc = 4MHz, Tosc = 1uS
// Prescale Timer0 by 1:32, one count per 32 uS
// This gives a Timer0 frequency of 1/(32uS*256) = 122 Hz
//
// Changed - frequency doubled
// Prescale Timer0 by 1:16, one count per 16 uS
// This gives a Timer0 frequency of 1/(16uS*256) = 244 Hz.
// Allows 16 instructions per count so be careful at very low or very high duty cycles



   // Set GP2 port to digital mode
   FOSC4 = 0;

   // Shut off AtoD, set pins as digital I/O
   ADCON0 = 0;

   // Set all GPIO pins to 0
   GPIO = 0;
   
   // Set GP0 as Output, GP1,2,3 as Input
   TRISGPIO = PWM_HIZ;

   updateSetting();
   TMR0 = 0;

//   debug();

   state = STATE_NORMAL;
   mode = MODE_OFF;

   while( 1 )
   {
      if( pwmSetting != 0 )
      {
         PWM_pin = 0;
         TRISGPIO = PWM_LOZ;
      }

   // If setting is more than 50% do new input processing immediately
   // because there is lots of time.
   // This effectively allows duty cycles from 0 to 100%

      if( pwmSetting.7 )
      {
         updateSetting();
         clrwdt();

         if( pwmSetting < 255 )
         {
            while( TMR0 < pwmSetting )
               ;
            TRISGPIO = PWM_HIZ;
         }
      }

   // else wait until end of output high pulse to do processing
      else
      {
         if( pwmSetting )
         {
            while( TMR0 < pwmSetting )
               ;
            TRISGPIO = PWM_HIZ;
         }

         updateSetting();
         clrwdt();
      }

      while( TMR0 )
         ;         
   }
}
